<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebGL Hello World</title>
</head>

<body>
  <canvas id="c"></canvas>
</body>
<script>
  const canvas = document.getElementById('c');
  var gl = canvas.getContext('webgl');
  if (!gl) {
    throw new Error('不能使用Web GL')
  }

  // 创建着色器方法，输入参数：渲染上下文，着色器类型，数据源
  function createShader(gl, type, source) {
    var shader = gl.createShader(type); // 创建着色器对象
    gl.shaderSource(shader, source); // 提供着色器数据源
    gl.compileShader(shader); // 编译-着色器
    var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!success) {
      console.log(gl.getShaderInfoLog(shader));
      gl.deleteShader(shader);
      throw new Error('Creating shader instance failed.')
    }
    return shader;
  }

  var verterxShaderSource = document.getElemengById('vertex-shader-2d').text;
  var fragmentShaderSource = document.getElementByid('fragment-shader-2d').text;
  var vertexShader = createShader(gl, gl.VERTEX_SHADER, verterxShaderSource);
  var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

  // 两个着色器link到program
  function createProgram(gl, vertexShader, fragmentShader) {
    var program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    var success = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (success) {
      return program;
    }
    console.log(gl.getProgramInfoLog(program));
    gl.deleteProgram(program);
  }


  // 调用它
  createProgram(gl, vertexShader, fragmentShader);

  // WegGL 主要任务就是设置好状态并为GLSL着色程序提供数据
  var positionAttributeLocation = gl.getAttributeLocation(program, "a_position");

  // 从缓冲中获取数据，所以我们创建一个缓冲
  var positionBuffer = gl.createBuffer();

  // WebGL 可以通过绑定点操控全局范围内的许多数据，你可以把绑定点想想成Web GL内部的全局变量。
  // 首先绑定一个数据源到绑定点，然后引用绑定点指向该数据源。所以我们来绑定位置信息缓冲（下面的绑定点就是ARRAY_BUFFER）

  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  // 现在可以通过绑定点向缓存中存放数据
  var positions = [
    0, 0,
    0, 0.5,
    0.7, 0
  ];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

  // 这里完成了一系列事情，第一件就是有一个javascript 序列positions. 然后Web GL需要一个强类型数据，
  // 所使用Float32Array(positions) 创建一个32位浮点型数据序列，并从positions中复制数据到序列中。
  // gl.bufferData()复制这些数据到GPU的positionsBuffer对象上。它最终传递到positionBuffer上是因为
  // 我们前一步中我们将它绑定到了ARRAY_BUFFER上
  // 最后一个参数STATIC_DRAW是Web GL最终怎么使用这个数据。Web GL根据一些提示做优化。gl.STATIC_DRAW
  // 提示Web GL我们不会经常改变这些数据

  /**
   * 渲染
   *
   * /
  
  // 我们需要告诉WebGL怎么提供的gl_Position裁切空间坐标对应画布像素坐标，通常我们把画布像素坐标也将屏幕空间。
  // 为了实现这个目的，我们只需要调用gl.viewport()并传递画布的当前尺寸
  
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

  // 这样就告诉Web GL 裁切空间从-1-1分别对应到x轴的 0->gl.canvas.width
  // 和y轴的0 -> gl.canvas.height
  // 清空画布
  gl.clearColor(0, 0, 0, 0);
  gl.clearColor(gl.COLOR_BUFFER_BIT);

  // 我们需要告诉WebGL运行那个着色程序
  gl.useProgram(program);

  // 接下来我们需要告诉Web GL怎么冲缓存区获取数据给着色器中的属性。首先我们需要启动相应属性
  gl.enableVertexAttribArray(positionAttributeLocation);

  // 然后从缓冲中读取数据的方式

  // 将绑定点绑定到数据
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  // 告诉属性怎么中positionBuffer中读取数据
  var size = 2; // 每次迭代运行提取2个单位数据
  var type = gl.FLOAT; // 每个单位数据类型是32位浮点
  var normize = false; // 不需要归一化数据
  var stride = 0; // 0 = 移动单位数 * 每个单位占用内存（sizeof(type)）
                  // 每次迭代运行运动多少内存到下一数据开始点
  var offset = 0; // 从缓冲起始位置开始读区
  gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);
  // 一个隐藏信息是gl.vertexAttribPointer是将属性绑定到当前的ARRAY_BUFFER。换句话说就是属性绑定到positionBuffer上，
  // 这也意味着现在利用绑定点随意将ARRAY_BUFFER绑定到其它数据上后，该数据依然从positionBuffer上读取数据

  // GLSL顶点着色器中注意到a_position属性的数据类型是vec4
  // vec4 是有四个浮点数据的数据类型，在js中你可以想象成a_position = { x: 0, y: 0, z: 0, w: 0 }
  // 设置size = 2, 属性默认值是0, 0, 0, 1, 然后属性将会从缓冲中获取前两个值x和y，z和w还是默认0,1

  // 我们终于可以用Web GL 运行GLSL着色器程序了
  var primitiveType = gl.TRIANGLES;
  var offset = 0;
  var count = 3;
  gl.drawArrays(primitiveType, offset, count);
  
  // 因为count=3, 所以顶点着色器运行3次。第一次运行会从缓冲数据位置读取前两个值给属性值a_position.x, a_position.y
  // 第二次运行会把后两个值给a_position.x, a_position.y, 最后一次运行将赋予最后两个值

  // 因为我们设置primitiveType为gl.TRIANGLES, 顶点着色器每次运行三次Web GL接会根据三个gl_position值绘制一个三角形，
  // 不论我们的画布大小是多少，在裁剪空间中每个方向的坐标范围都是-1和1

  // 由于我们的顶点着色器仅仅是传递位置缓冲中的值给gl_position,所以三角形在裁剪空间中的坐标如下
  // 0,0
  // 0,0.5
  // 0.7,0

  // WebGL 会在裁剪空间转换到屏幕空间并在屏幕空间中绘制一个三角形，如果画布大小是400*300我们会得到

  // 0,0   => 200，150
  // 0,0.5 => 200, 225
  // 0.7,0 => 340, 150

  // 现在的WebGL将渲染出这个三角形。绘制每个像素的时候WebGL 都将调用我们的片段着色器。
  // 我们的片段着色器只是简单的设置了gl_FragColor,1, 0, 0.5,1, 由于画布的每个通道宽度为8位，
  // 这表示Web GL最终在画布上绘制的[255, 0, 127, 255]


</script>
<script id="vertex-shader-2d" type="notjs">
  // 一个属性值，将会从缓冲中获取数据
  attribute vec4 a_position;
  // 所以着色器都有一个main方法
  void main() {
    // gl_Position 是一个顶点着色器主要设置的变量
    gl_Position = a_position;
  }
</script>
<script id="fragment-shader-2d" type="notjs">
  // 片段着色器默认没有精度，我们设置一个默认精度
  // mediump 是一个不错的精度medium precision(中等精度)
  precision mediump float;
  void main() {
    // gl_FragColor 是一个片段着色器主要设置的变量
    gl_FragColor = vec4(1, 0, 0.5, 1); // 返回紫红色
    // 1代表红色，0 代表绿色，0.5 代表蓝色，1代表阿尔法通道值
  }
</script>
<script>
  // gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
  // gl.enableVertexAttribArray(a_positionLoc)
  // gl.vertexAttribPointer(a_positionLoc, positionNumComponent, gl.FLOAT, false, 0, 0)

  // gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer)
  // gl.enableVertexAttribArray(a_normalLoc)
  // gl.vertexAttribPointer(a_normalLoc, normalNumComponent, gl.FLOAT, false, 0, 0)
  
</script>


</html>